/*
 * RDT 3.0 Receiver
 */
#include "np.h"
#include "packet.h"

extern int DEBUG_ON;	// print debug messages if set
static char *port = "5678";		// default port number

//Packet buffers
static Packet 	sndPacket, rcvPacket;

// Performance Measure
static int nPackets, nCorrectPackets, nCorruptPackets, nDupPackets;
static int nCorrectBytes;
static struct timeval startTime, endTime;		// gettimeofday()

void printStatistics()
{
	struct timeval interval;
	float duration; 		// in sec
	float throughput;		// in bytes per sec

	fprintf(stderr, "Total packets received: %d\n", nPackets);
	fprintf(stderr, "\tdulplicate packets: %d\n", nDupPackets);
	fprintf(stderr, "\tcorrupted packets: %d\n", nCorruptPackets);
	fprintf(stderr, "\tcorrect packets:%d\n", nCorrectPackets);
	timersub(&endTime, &startTime, &interval);
	duration = ((float)interval.tv_sec) + interval.tv_usec/1000000.;
	throughput = nCorrectBytes / duration;
	fprintf(stderr, "Total bytes correctly transfered: %d\n",
			nCorrectBytes);
	fprintf(stderr, "Total time elapsed: %f sec\n", duration);
	fprintf(stderr, "Throughput: %f Bytes/sec\n", throughput);
	return;
}

void deliver_data( char *data, int datalen)
{
	*(data+datalen) = '\0';
	fputs(data, stdout);
	return;
}

int main( int argc, char *argv[] )
{
	int s;
	struct sockaddr_in local, sender;
	unsigned char rcvSeq;
	Packet	*sndpkt = &sndPacket;
	Packet *rcvpkt = &rcvPacket;
	char	*data = &(rcvPacket.data[0]);
	socklen_t		senderlen;
	static	int		once = 1;
	int opt;

	while ( (opt = getopt(argc, argv, "d")) != -1) {
		switch (opt) {
		case 'd':
			DEBUG_ON = 1; 	// Debug mode
			break;
		default:
			error(1, 0, "Usage: %s [-d] [port]\n");
		}
	}
	if (optind == argc-1) {
				port = argv[optind];
	}
	debug("Port = %s\tDebugMode = %s\n", port, DEBUG_ON ? "on" : "off");

	set_address( NULL, port, &local, "udp" );	// Set my all addresses
	s = socket( AF_INET, SOCK_DGRAM, 0 );
	if (  s < 0 )
		error( 1, errno, "socket call failed\n" );

	if ( bind( s, ( struct sockaddr * ) &local, sizeof( local ) ) < 0 )
		error( 1, errno, "bind failed\n" );

	debug("[END] send Packet : type[%d] seq[%d] len[%d] checksum[%d] \n",  controlPacket.header.type, s_seqm controlPacket.header.len, controlPacket.header.checksum);
	return 0;

	rcvSeq = 0;
	sndpkt = make_pkt(ACK, rcvSeq, &sndPacket, 0);

	for (;;)
	{
		int 	datalen, rcvlen;

		senderlen = sizeof(sender);
		rcvlen = recvfrom(s, rcvpkt, sizeof(Packet), 0,
				(struct sockaddr *) &sender,  (socklen_t *) &senderlen);
		if ( rcvlen < 0)
			error (1, errno, "recvfrom failed\n");
		if ( once ) {
			once = 0;
			if ( gettimeofday( &startTime, NULL ) < 0 )	// start of RDT session
				error( 1, errno, "gettimeofday failure" );
		}
		print_pkt("<<", rcvpkt);
		nPackets++;

		// Corrupted packet
		if ( isCorrupt(rcvpkt) ) {
			connect(serv_sock, (struct sockaddr *)&clnt_addr, clnt_addr_size);
			make_cpkt(&controlPacket, ACK, ack);
			write(1, dataPacket.data, dataPacket.header.len-HEADER_LEN);

			continue;
		}
		// Not corrupted packet
		if ( isType(rcvpkt, DATA, rcvSeq) ) { // DATA packet
			error(1, errno, "ACK udt_send error\n");

			debug("[ACK] send Packet : type[%d] seq[%d] len[%d] checksum[%d] \n",  controlPacket.header.type, s_seqm controlPacket.header.len, controlPacket.header.checksum);
			correct_pkt++;
			rcv_pkt++;

			rcv_byte=rcv_byte + dataPacket.header.len - HEADER_LEN;
			pre_seq=r_seq;
			r_seq++:
			ack = r_seq+1;

			continue;;
		}
		// END packet
		if ( isType(rcvpkt, END, rcvSeq) ) {
			// Reply ACK
			connect(serv_sock, (struct sockaddr *)&clnt_addr, clnt_addr_size);
			make_cpkt(&controlPacket, ACK, ack);
			write(1, dataPacket.data, dataPacket.header.len-HEADER_LEN);

			// To ensure ACK packet delivery
			udt_send(s, sndpkt, (struct sockaddr *)&sender, senderlen);
			udt_send(s, sndpkt, (struct sockaddr *)&sender, senderlen);
			udt_send(s, sndpkt, (struct sockaddr *)&sender, senderlen);
			if ( gettimeofday( &endTime, NULL ) < 0 )	// End of RDT session
				error( 1, errno, "gettimeofday failure" );
			nPackets--;		// Do not count this END packet
			printStatistics();
			exit(0);
		}

		else {
		// Duplicated packet

			debug("[ACK] send Packet : type[%d] seq[%d] len[%d] checksum[%d] \n",  controlPacket.header.type, s_seqm controlPacket.header.len, controlPacket.header.checksum);
						correct_pkt++;
						rcv_pkt++;
			continue;
		}
	} // End of for loop//

}
